﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework;
using XcoAppSpaces.Transport.Sockets.Auth;
using XcoAppSpaces.Transport.Sockets.Auth.Contract;
using System.Threading;
using System.Net.Sockets;
using System.Threading.Tasks;

namespace test.XcoAppSpaces.Transport.Sockets.Auth
{
	[TestFixture]
	public class SendChallengeTests
	{
		private SendChallenge sut;
		private ConnectionWithChallenge validResult;
		private TcpClient invalidResult;

		[SetUp]
		public void SetUp()
		{
			sut = new SendChallenge();
			sut.Inject(new SecuritySettings { ChallengeLength = 15 });
			sut.Out_ValidConnection += _ => validResult = _;
			sut.Out_InvalidConnection += _ => invalidResult = _;
			validResult = null;
			invalidResult = null;
		}

		[Test]
		public void ChallengeSent()
		{
			using (var connections = new ConnectedTcpClientPair())
			{
				sut.In_Connection(connections.ReceivingClient.GetConnectionInfo());
				Assert.IsTrue(SpinWait.SpinUntil(() => validResult != null, 1000));

				var received = NetworkStreamExtensions.ReadExactly(connections.SendingClient.GetStream(), 15);
				Assert.AreEqual(15, received.Length);
				Assert.AreSame(connections.ReceivingClient, validResult.Connection.Client);
				Assert.AreEqual(validResult.Challenge, received);
			}
		}

		[Test]
		public void ChallengeSent_DiffersWhenSendingMultipleTimes()
		{
			using (var connections = new ConnectedTcpClientPair())
			{
				sut.In_Connection(connections.ReceivingClient.GetConnectionInfo());
				Assert.IsTrue(SpinWait.SpinUntil(() => validResult != null, 1000));

                var received1 = NetworkStreamExtensions.ReadExactly(connections.SendingClient.GetStream(), 15);


				validResult = null;
				sut.In_Connection(connections.ReceivingClient.GetConnectionInfo());
				Assert.IsTrue(SpinWait.SpinUntil(() => validResult != null, 1000));

                var received2 = NetworkStreamExtensions.ReadExactly(connections.SendingClient.GetStream(), 15);

				Assert.IsFalse(ByteExtensions.ArrayEquals(received1, received2));
			}
		}

		[Test]
		public void ConnectionInvalidIfChallengeCannotBeSent()
		{
			using (var connections = new ConnectedTcpClientPair())
			{
				var connInfo = connections.ReceivingClient.GetConnectionInfo();
				connections.ReceivingClient.Client.Close();
				Thread.Sleep(100);

				sut.In_Connection(connInfo);
				Assert.IsTrue(SpinWait.SpinUntil(() => invalidResult != null, 1000));
				Assert.AreSame(connections.ReceivingClient, invalidResult);
			}
		}
	}
}
